﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using log4net;

namespace mimosa.Udp
{
    public delegate void UdpReceivedDelegate(IPEndPoint remoteEndPoint, byte[] buffer);
    class Udp_State
    {
        public UdpClient UdpClient
        {
            get;
            set;
        }

        public IPEndPoint InEndPoint
        {
            get;
            set;
        }
    }

    public class Udp_Server
    {
        public event UdpReceivedDelegate UdpReceived;
        public static ManualResetEvent allDone = new ManualResetEvent(false);
        public ILog logger = null;
        UdpClient udpClient = null;
        string ipString;
        int port;
        bool IsRunning = false;


        public IPEndPoint SIPChannelEndPoint
        {
            get;
            set;
        }

        public Udp_Server(string ipString, int port)
        {
            this.ipString = ipString;
            this.port = port;
            //Initialize();
        }

        public void Initialize()
        {
            try
            {
                //logger.Info(string.Format("Init Udp Server {0}:{1}",this.ipString, this.port));
                Console.WriteLine(string.Format("Init Udp Server {0}:{1}", this.ipString, this.port));
                SIPChannelEndPoint = new IPEndPoint(IPAddress.Parse(this.ipString), this.port);
                udpClient = new UdpClient(SIPChannelEndPoint);
            }
            catch (Exception e)
            {
                //logger.Error(e.Message);
                Console.WriteLine(e.Message);
            }
        }

        public void BeginListener()
        {
            IsRunning = true;
            Thread listenThread = new Thread(new ThreadStart(Listen));
            listenThread.Start();
        }

        void Listen()
        {
            Udp_State udpState = new Udp_State();
            udpState.UdpClient = this.udpClient;
            udpState.InEndPoint = new IPEndPoint(IPAddress.Any, 0);
            if (udpClient == null)
            {
                logger.Error("UdpClient is null");
                return;
            }
          
            IsRunning = true;
            while (IsRunning)
            {
                allDone.Reset();
                udpClient.BeginReceive(new AsyncCallback(ReceiveCallback), udpState);
                allDone.WaitOne();
            }
        }

        void ReceiveCallback(IAsyncResult ar)
        {
            Udp_State udpState = (Udp_State)ar.AsyncState;
            try
            {
                IPEndPoint remoteEP = udpState.InEndPoint;
                Byte[] receivedData = udpState.UdpClient.EndReceive(ar, ref remoteEP);

                UdpReceived(remoteEP, receivedData);
            }
            catch (SocketException e)
            {
                Console.WriteLine(e.Message); 
            }
                
        }

        public void Send(IPEndPoint destinationEndPoint, string message)
        {
            byte[] messageBuffer = Encoding.UTF8.GetBytes(message);
            Send(destinationEndPoint, messageBuffer);
        }

        public void Send(IPEndPoint destinationEndPoint, byte[] buffer)
        {
            try
            {
                if (destinationEndPoint == null)
                {
                    logger.Error("An empty destintation was specified to Send in SIPChannel.");
                }

                udpClient.Send(buffer, buffer.Length, destinationEndPoint);
            }
            catch (ObjectDisposedException)
            {
                //logger.Warn("The SIP channel was not accessible when attempting to send a message to, " + IPSocket.GetSocketString(destinationEndPoint) + ".");
            }
            catch (Exception excp)
            {
                //logger.Error("Exception (" + excp.GetType().ToString() + ") SIPChannel Send (sendto=>" + IPSocket.GetSocketString(destinationEndPoint) + "). " + excp.Message);
                throw excp;
            }
        }		

    }
}
